using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace PrisonStep
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class PrisonGame : Microsoft.Xna.Framework.Game
    {
        #region Fields

        /// <summary>
        /// This graphics device we are drawing on in this assignment
        /// </summary>
        GraphicsDeviceManager graphics;

        /// <summary>
        /// The camera we use
        /// </summary>
        private Camera camera;

        /// <summary>
        /// The player in your game is modeled with this class
        /// </summary>
        private Player player;

        /// <summary>
        /// This is the actual model we are using for the prison
        /// </summary>
        private List<PrisonModel> phibesModel = new List<PrisonModel>();

        /// <summary>
        /// percetage of slime effect
        /// </summary>
        private float greeness = 0;

        /// <summary>
        /// flag for whether a door is completely open or not
        /// </summary>
        bool open = false;

        PhibesModel phibesModelMap;

        private Vector3 mouseWorldCoordinates;
        public Vector3 MouseWorldCoordinates { get { return mouseWorldCoordinates; } }


        /// <summary>
        /// Location of the 6 doors (the first door's position has been adjusted to make it more centered)
        /// </summary>
        private Vector2[] doorLoc = { new Vector2(218, 1053), 
            new Vector2(-11, -769), 
            new Vector2(587, -999), 
            new Vector2(787, -763), 
            new Vector2(1187, -1218) };

        #endregion

        #region Properties

        private SmokePlumeParticleSystem3d smoke = null;

//        public double tillBlow;
//       private Viewport viewport;
//        public double tillGone = 0;
//        public double tillPuff = 0;
//        public Vector3 puffWhere;

//        private LinkedList<Vector2> toBlow = new LinkedList<Vector2>();

        /// <summary>
        /// The game camera
        /// </summary>
        public Camera Camera { get { return camera; } }

        /// <summary>
        /// Percetage of slime effect
        /// </summary>
        public float Greeness { get { return greeness; } set { greeness = value; } }

        /// <summary>
        /// Return the positions on the doors
        /// </summary>
        public Vector2[] DoorLocations { get { return doorLoc; } }

        public GraphicsDeviceManager Graphics { get { return graphics; } set { graphics = value; } }

        public SmokePlumeParticleSystem3d Smoke { get { return smoke; } }

        /// <summary>
        /// The PhibesModel
        /// </summary>
        public List<PrisonModel> PhibesModel { get { return phibesModel; } }

        MouseState mouseState;

        #endregion


        /// <summary>
        /// Constructor
        /// </summary>
        public PrisonGame()
        {
            // XNA startup
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            // Create objects for the parts of the ship
            for (int i = 1; i <= 6; i++)
            {
                phibesModel.Add(new PrisonModel(this, i));
            }

            phibesModelMap = new PhibesModel(this);

            // Create a player object
            player = new Player(this, phibesModelMap);

            // Some basic setup for the display window
            this.IsMouseVisible = true;
            this.Window.AllowUserResizing = true;
            this.graphics.PreferredBackBufferWidth = 1024;
            this.graphics.PreferredBackBufferHeight = 728;

            // Basic camera settings
            camera = new Camera(graphics, phibesModelMap);
            camera.FieldOfView = MathHelper.ToRadians(42);
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            camera.Initialize();

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            player.LoadContent(Content);
            smoke = new SmokePlumeParticleSystem3d(10);
            smoke.LoadContent(Content);
            

            foreach (PrisonModel model in phibesModel)
            {
                model.LoadContent(Content);
            }
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }


        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed ||
                Keyboard.GetState().IsKeyDown(Keys.Escape))
                this.Exit();

            /*
            mouseState = Mouse.GetState();
            if (mouseState.LeftButton == ButtonState.Pressed)
            {
                SetMouseWorldCoordinates();
            }
            */
            //if (mouseState.

            //
            // Update game components
            //

            //open is whether a door is open or not...it really should be a property
            player.Update(gameTime, open);

            //Update each prison model section and check their doors
            open = false;   //assume all the doors are closed initally
            foreach (PrisonModel model in phibesModel)
            {
                // player.OpenDoor is whether a door should be opening or closing
                // player.Door is which door the player is neer
                // this probably should have just been a public Player property
                model.Update(gameTime);

                //if this model has a door open, update the flag to true
                if (model.IsOpen)
                    open = true;
            }


            smoke.Update(gameTime.ElapsedGameTime.TotalSeconds);
            camera.Update(gameTime);
            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            //this line fixes the spritebatch graphics issues
            GraphicsDevice.DepthStencilState = DepthStencilState.Default;

            graphics.GraphicsDevice.Clear(Color.Black);

            foreach (PrisonModel model in phibesModel)
            {
                model.Draw(graphics, gameTime);
            }

            smoke.Draw(GraphicsDevice, camera);
            player.Draw(graphics, gameTime);
            base.Draw(gameTime);
        }

        public void SetMouseWorldCoordinates()
        {

            Vector3 nearSource = new Vector3(mouseState.X, mouseState.Y, 0.0f);
            Vector3 farSource = new Vector3(mouseState.X, mouseState.Y, camera.ZNear);

            Vector3 nearPoint = graphics.GraphicsDevice.Viewport.Unproject(nearSource, camera.Projection, camera.View, Matrix.Identity);
            Vector3 farPoint = graphics.GraphicsDevice.Viewport.Unproject(farSource, camera.Projection, camera.View, Matrix.Identity);

            Vector3 direction = farPoint - nearPoint;
            direction.Normalize();

            Ray r = new Ray(nearPoint, direction);

            player.MouseCollision(nearPoint, direction, mouseState);
            
            
            Vector3 n = new Vector3(0f, 1f, 0f);
            Plane p = new Plane(n, 0f);

            float denominator = Vector3.Dot(p.Normal, r.Direction);
            float numerator = Vector3.Dot(p.Normal, r.Position) + p.D;
            float t = -(numerator/denominator);
            
            //mouseWorldCoordinates = nearPoint + direction * t;
            //Vector3 temp = nearPoint + direction * t;
            //player.MousePressed(mouseWorldCoordinates.X, mouseWorldCoordinates.Z);
        }

        public double[] lightData =
            {   1,      568,      246,    1036,   0.53,   0.53,   0.53,     821,     224, 
              941,  14.2941,       45, 43.9412,    814,    224,   1275,    82.5,       0,  0,
                2,       -5,      169,     428, 0.3964,  0.503, 0.4044,    -5.4,     169,
             1020, 255, 20, 147,   -5.4,    169,   -138, 37.8275,      91, 91,
                3,      113,      217,    -933,    0.5,      0,      0,    -129,     185,
            -1085,	     50,        0,       0,    501,    185,  -1087,      48,       0,  0,
                4,      781,      209,    -998,    0.2, 0.1678, 0.1341,    1183,     209,
             -998,	     50,  41.9608, 33.5294,    984,    113,   -932,       0,      80,  0,
                5,      782,      177,    -463,   0.65, 0.5455, 0.4359,     563,     195,
             -197,	     50,        0,       0,   1018,    181,   -188,      80,       0,  0,
                6,     1182,      177,   -1577,   0.65, 0.5455, 0.4359,     971,     181,
            -1801,        0,  13.1765,      80,   1406,    181,  -1801,       0, 13.1765,  80};

        /// <summary>
        /// helper function to get the light data with the section and position/color offset
        /// For itemes: even is position and  odd is color
        /// </summary>
        /// <param name="section"></param>
        /// <param name="item"></param>
        /// <returns></returns>
        public Vector3 LightInfo(int section, int item)
        {
            int offset = (section - 1) * 19 + 1 + (item * 3);
            return new Vector3((float)lightData[offset], (float)lightData[offset + 1], (float)lightData[offset + 2]);
        }

//        public void ExplosionAt(Vector3 where)
//        {
//            Vector3 e1 = viewport.Project(where, camera.Projection, camera.View, Matrix.Identity);
//            toBlow.AddLast(new Vector2(e1.X, e1.Y));
//            tillBlow = 0;
//        }
    }
}
